#include "Data.h"


/* Verifica se o caracter 'c' é um separador */

int isSeparador(char c, char separador[])
{
    if (strchr(separador,c) != NULL || c == EOF)
        return 1;
    else
        return 0;
}


/* Cria uma string começando no byte_inicio do arquivo e terminando */
/* em byte_fim, e a armazena em partição.							*/

void cria_Particao(FILE *arq, char **particao, int byte_inicio, int byte_fim)
{
	/* Aloca o tamanho necessário para a partiçao */
    *particao = (char *) malloc( (byte_fim-byte_inicio+1)*sizeof(char)); 

    /* Leitura de byte_inicio até byte_fim-byte_inicio do arquivo */
    fseek(arq, byte_inicio, SEEK_SET);
    fread(*particao, sizeof(char), byte_fim-byte_inicio, arq);

    (*particao)[byte_fim-byte_inicio] = '\0';
}


/* Percorre o arquivo até achar um separador. */
/* O retorno é o offset do ultimo caracter antes do separador ou o EOF. */

int busca_UltimoSeparador(FILE *arq, int offset, char separador[])
{
    char c = 0;

    if(offset == 0)
    	return 0;

    fseek(arq, offset, SEEK_SET);
    
	/* Percorre o arquivo até achar um separador */    
	do
    {
        c = fgetc(arq);
    }
    while(!isSeparador(c, separador));
    
    if (c == EOF)
    	return ftell(arq);
    else
        return ftell(arq)-1;
}


/* Percorre uma string até achar um separador. */
/* O retorno é o offset do ultimo caracter antes do separador ou o EOF. */

int busca_UltimoSeparadorString(char *str, int indice, char separador[])
{
    long int i=0;
    
    if (indice == 0)
        return 0;

    for(i=indice; !isSeparador(str[i],separador) || str[i]!='\0'; i++);

    return i;
}


/* Realoca a lista de primos caso seja ela estoure MAXLENGTH */

void realloc_list (primos **p, int novo_tamanho)
{
    int *r;

    r = realloc ((*p)->prime_list, sizeof(int)*novo_tamanho);
    (*p)->prime_list = r;   
}


/* Armazena em 'palavranova' a string 'palavra' sem o char 'caracter' */

void removeCaracter(char* palavra, char caracter, char* palavranova){
    char p;
    int i = 0, j = 0;

    do{
        p = palavra[i];

        if(p!=caracter)
		{
            palavranova[j]=p;
            j++;
        }

        i++;
    }while(p!='\0');
}


/* Verifica se 'palavra' é um palíndromo.					*/
/* Retorna a soma ASCII da palavra caso seja um palíndromo. */
/* Retorna um número negativo caso contrário. 				*/

int verificaPalindromo(char* palavra, int PARALLEL)
{
    int tam, i, limite, ascii, somaascii = 0, ret = 1;
    
    tam=strlen(palavra);
    limite=floor((double) (tam)/ (double) 2);
    
	for(i=0; i<limite; i++)
	{
	    if(palavra[i]!=palavra[tam-1-i])
		{ 
	        if((abs(palavra[i]-palavra[tam-1-i])!=32) || palavra[i]<65 || palavra[tam-1-i]<65) return -1;
	    }

		somaascii+=palavra[i]+palavra[tam-1-i];
	}	

    if(tam%2 == 1) 
	{
        ascii=palavra[limite];
        somaascii+=ascii;
    }

    return somaascii;
}


/* Imprime todos os palíndromos de palavra existentes na string 'str' */
/* Armazena em um arquivo separado para palavras */

void imprimePalindromos_Palavra(char *str, char separador[], primos *list, FILE *palind_palavra, int parallel)
{
    char *palin_candidate, *str_palavra;    
    int ascii_palindromo = 0;
    separador[8] = ' ';
    
    /* String usada como cópia para que a string original não seja destruída pelo strtok() */
    str_palavra = (char *) malloc((strlen(str)+1)*sizeof(char));
    strcpy(str_palavra,str);

    /* Strtok(): Percorre palavra por palavra na string utilizando os caracteres separadores no vetor 'separador' */
    palin_candidate = strtok((char *) str_palavra, separador);
    

	/* Verifica palíndromos enquanto não chegar no final de 'str' */
    while(palin_candidate != NULL)
    {
        if(strlen(palin_candidate) > 1)
        {
            ascii_palindromo = verificaPalindromo(palin_candidate, parallel);
			
			/* 'ascii_palindromo' é negativo caso o candidato a palíndromo não seja */                
			if(ascii_palindromo > 0)
            {
                if(crivo(list, ascii_palindromo, parallel))
                    fprintf(palind_palavra, "Palavra palindromo:%s\t\tSoma ASCII: %d\tÉ primo\n", palin_candidate, ascii_palindromo);
                else
                    fprintf(palind_palavra, "Palavra palindromo:%s\t\tSoma ASCII: %d\tNao primo\n", palin_candidate, ascii_palindromo);
                
            }
        }

        palin_candidate = strtok(NULL,separador);
    }

    free(str_palavra);
    separador[8] = '\0';
}


/* Imprime todos os palíndromos de frase existentes na string 'str' */
/* Armazena em um arquivo separado para frases */

void imprimePalindromos_Frase(char *str, char separador[], primos *list, FILE *palind_frase, int parallel)
{
    char *palin_candidate, *str_frase, *str_sem_espaco;
    int ascii_palindromo=0;

	/* String usada como cópia para que a string original não seja destruída pelo strtok() */
    str_frase = (char *) malloc((strlen(str)+1)*sizeof(char));
    strcpy(str_frase,str);

    /* Strtok(): Percorre palavra por palavra na string utilizando os caracteres separadores no vetor 'separador' */
    palin_candidate = strtok((char *) str_frase, separador);

	/* Aloca a string que armazenará a frase sem espaços */
	str_sem_espaco = (char *) malloc(MAXLENGTH * sizeof(char));
    
	/* Verifica palíndromos enquanto não chegar no final de 'str' */
    while(palin_candidate != NULL)
    {
        removeCaracter(palin_candidate, ' ', str_sem_espaco);

        if(strlen(str_sem_espaco) > 1)
        {
            ascii_palindromo = verificaPalindromo(str_sem_espaco, parallel);

			/* 'ascii_palindromo' é negativo caso o candidato a palíndromo não seja */ 
            if(ascii_palindromo > 0)
            {
                if(crivo(list, ascii_palindromo, parallel))
                    fprintf(palind_frase, "Frase palindromo:%s\t\tSoma ASCII: %d\tÉ primo\n", palin_candidate, ascii_palindromo);
                else
                    fprintf(palind_frase, "Frase palindromo:%s\t\tSoma ASCII: %d\tNao primo\n", palin_candidate, ascii_palindromo);

            }
        }

        palin_candidate = strtok(NULL,separador); 
    }

	free(str_sem_espaco);
}


/* Algoritmo Crivo de Eratóstenes: 							  */
/*   Armazena todos os primos abaixo de 'valor' em uma lista. */
/*	 A função retorna true se é primo ou false caso não seja. */

int crivo(primos *list, int valor, int PARALLEL)
{ 
    int i, j;
    int *vetor, prime;
	int meio, inf = 0, pos = 0;

	/* Caso o valor seja maior que o último primo da lista, 		 */	
	/* mais primos podem existir e ser inseridos na lista de primos  */    
	if(valor > (list->last_prime))
    {
        int tamanho = valor-(list->last_prime);

        vetor = malloc(tamanho*sizeof(int));

		/* Vetor auxiliar com todos os valores acima do último primo e abaixo de 'valor' */
        /* VERSÃO PARALELA */
		if(PARALLEL == 1)
		{
			#pragma omp parallel for
		    for(i=0; i<tamanho; i++)
		        vetor[i] = (list->last_prime)+i+1;
		}
		/* VERSÃO SEQUENCIAL */
		else
		{
			for(i=0; i<tamanho; i++)
		        vetor[i] = (list->last_prime)+i+1;
		}

                        
		/* Encontra os múltiplos da lista de primos em 'vetor'. */
		for(i=0; i<=(list->last_index); i++)
        {
            prime = list->prime_list[i];
            
			/* Não encontra mais múltiplos quando o primo em 'prime' ao quadrado for maior que 'valor' */			
			if((prime*prime) <= valor)
			{            
				if(PARALLEL == 1)
				{				
					#pragma omp parallel for
				    for(j=0; j<tamanho; j++)
				    {
				        /* Se for múltiplo, substitui por -1 */
				        if(vetor[j]%prime == 0)
				            vetor[j] = -1;                          
				    }
				}
				else
				{		
				    for(j=0; j<tamanho; j++)
				    {
				        /* Se for múltiplo, substitui por -1 */
				        if(vetor[j]%prime == 0)
				            vetor[j] = -1;                          
				    }
				}
			}

            /* Após percorrer a lista inicial de primos, adiciona os novos primos de 'vetor' na lista */
            if(i == (list->last_index))
            {
                int posicao = 0;

                while(posicao < tamanho && vetor[posicao] == -1)
                    posicao++;

                /* Um novo valor não marcado foi achado */
                if(posicao < tamanho)
                {
                    (list->last_index)++;
                    list->prime_list[list->last_index] = vetor[posicao];
                    list->last_prime = vetor[posicao];                              
                    vetor[posicao]=-1; 
					
					/* Caso a lista esteja no limite, ela é realocada */
                    if(list->last_index == (list->max_len)-1)
                    {
                        int novo_tamanho = (list->max_len)*2;
                        realloc_list(&list, novo_tamanho);
                        list->max_len = novo_tamanho;
                    }                  
                }          
            }
        }       
            
    }       
    
    /* Verifica se 'valor' esta na lista de primos após atualiza-lá */
    pos  = list->last_index;
    while(inf <= pos)
    {
        meio = (inf+pos)/2;

        if(list->prime_list[meio] == valor)
        {
            return 1;
        }
        else if (list->prime_list[meio] < valor)
            inf = meio+1;
        else
            pos = meio-1;
    }

    return 0;
}



